-- $Id: $
-- File name:   tb_INTERCEPTOR.vhd
-- Created:     12/6/2010
-- Author:      Ryan McLean
-- Lab Section: 4
-- Version:     1.0  Initial Test Bench

library ieee;
library ece337_ip;
use ieee.std_logic_1164.all;
use ece337_ip.all;
use std.textio.all;

entity tb_INTERCEPTOR is
  generic(
    filename: string :="./test/encoded.dat";
    period : time := 8.34 ns;
    fullPacketLen : integer := 40;
    fullBitLen : integer := 8;
    tb_init_filename	: string := "mem_init.dat";
    tb_dump_filename	: string := "mem_dump.dat"
    );
end tb_INTERCEPTOR;

architecture TEST of tb_INTERCEPTOR is
  function INT_TO_STD_LOGIC( X: INTEGER; NumBits: INTEGER )
     return STD_LOGIC_VECTOR is
    variable RES : STD_LOGIC_VECTOR(NumBits-1 downto 0);
    variable tmp : INTEGER;
  begin
    tmp := X;
    for i in 0 to NumBits-1 loop
      if (tmp mod 2)=1 then
        res(i) := '1';
      else
        res(i) := '0';
      end if;
      tmp := tmp/2;
    end loop;
    return res;
  end;
  
  -- converts a character into a std_logic
  function to_std_logic(c: character) return std_logic is 
      variable sl: std_logic;
      begin
        case c is
          when 'U' => 
             sl := 'U'; 
          when 'X' =>
             sl := 'X';
          when '0' => 
             sl := '0';
          when '1' => 
             sl := '1';
          when 'Z' => 
             sl := 'Z';
          when 'W' => 
             sl := 'W';
          when 'L' => 
             sl := 'L';
          when 'H' => 
             sl := 'H';
          when '-' => 
             sl := '-';
          when others =>
             sl := 'X'; 
      end case;
     return sl;
    end to_std_logic;
  
  
  -- converts a string into std_logic_vector
  function to_std_logic_vector(s: string) return std_logic_vector is 
    variable slv: std_logic_vector(s'high-s'low downto 0);
    variable k: integer;
  begin
     k := s'high-s'low;
    for i in s'range loop
       slv(k) := to_std_logic(s(i));
       k      := k - 1;
    end loop;
    return slv;
  end to_std_logic_vector;                                       
  
  component INTERCEPTOR
    PORT(
         CLK : in std_logic;
         RST_N : in std_logic;
         LOG : in std_logic;
         D_PLUS_DEVICE_IN : inout std_logic;
         D_MINUS_DEVICE_IN : inout std_logic;
         D_PLUS_HOST_OUT : inout std_logic;
         D_MINUS_HOST_OUT : inout std_logic;
        -- MEMWAIT : in std_logic;
         S_DATA : inout std_logic_vector(7 downto 0);
         ADDR : out std_logic_vector(15 downto 0);
         RE : out std_logic;
         WE : out std_logic
    );
  end component;
  
  component scalable_off_chip_sram is
    generic(
      -- Memory Model parameters
      ADDR_SIZE_BITS	: natural	:= 16;	-- Address bus size in bits/pins with addresses corresponding to 
                                      --   the starting word of the accesss
      WORD_SIZE_BYTES	: natural	:= 1;	-- Word size of the memory in bytes
      DATA_SIZE_WORDS	: natural	:= 1;	-- Data bus size in "words"
      READ_DELAY	: time	:= 10 ns;	    -- Delay/latency per read access (total time between start of supplying address and when the data read from memory appears on the r_data port)
                                      -- Keep the 10 ns delay for on-chip SRAM
      WRITE_DELAY	: time	:= 10 ns		   -- Delay/latency per write access (total time between start of supplying address and when the w_data value is written into memory)
                                      -- Keep the 10 ns delay for on-chip SRAM
    );
    port(
      -- Test bench control signals
      mem_clr				: in	boolean;
      mem_init			: in	boolean;
      mem_dump			: in	boolean;
      verbose				: in	boolean;
      init_filename	: in 	string;
      dump_filename	: in 	string;
      start_address	: in	natural;
      last_address	: in	natural;
          
      -- Memory interface signals
      r_enable	: in		std_logic;
      w_enable	: in		std_logic;
      addr			: in		std_logic_vector((addr_size_bits - 1) downto 0);
      data			: inout	std_logic_vector(((data_size_words * word_size_bytes * 8) - 1) downto 0)
    );
  end component scalable_off_chip_sram;

  signal CLK : std_logic;
  signal RST_N : std_logic;
  signal LOG : std_logic;
  signal D_PLUS_DEVICE : std_logic;
  signal D_MINUS_DEVICE : std_logic;
  signal D_PLUS_HOST : std_logic;
  signal D_MINUS_HOST : std_logic;
 -- signal MEMWAIT : std_logic;
  signal S_DATA : std_logic_vector(7 downto 0);
  signal ADDR : std_logic_vector(15 downto 0);
  signal RE : std_logic;
  signal WE : std_logic;

  signal file_line : std_logic_vector(7 downto 0);
  signal EOF : std_logic;
  signal EOL : std_logic;
  
  file infile: TEXT open read_mode is filename;
  
  signal SYNC : std_logic_vector(7 downto 0) := "10000000";
  signal PID : std_logic_vector(7 downto 0) := "00111100";
  signal DATA : std_logic_vector(7 downto 0);
  signal CRC : std_logic_vector(15 downto 0) := "ZZZZZZZZZZZZZZZZ";
  signal FULL_PACKET : std_logic_vector(39 downto 0);
  
  signal DATA_OE: std_logic;
  
  signal tb_mem_clr : boolean;
  signal tb_mem_init : boolean;
  signal tb_mem_dump : boolean;
  signal tb_verbose : boolean;
  signal tb_start_address	: natural;
  signal tb_last_address	: natural;
  
  signal bit_count : integer := 0;

begin
  DUT: INTERCEPTOR 
    port map(
      CLK => CLK,
      RST_N => RST_N,
      LOG => LOG,
      D_PLUS_DEVICE_IN => D_PLUS_DEVICE,
      D_MINUS_DEVICE_IN => D_MINUS_DEVICE,
      D_PLUS_HOST_OUT => D_PLUS_HOST,
      D_MINUS_HOST_OUT => D_MINUS_HOST,
     -- MEMWAIT => MEMWAIT,
      S_DATA => S_DATA,
      ADDR => ADDR,
      RE => RE,
      WE => WE
    );
  -- An example of how to map an instance of the on-chip scalable sram model (Taken from my test bench)
  MEM: scalable_off_chip_sram
    generic map (
      -- Memory interface parameters
      READ_DELAY			=> (period - 2 ns),	-- CLK is 2 ns longer than access delay for conservative padding for flipflop setup times and propagation delays from the external SRAM chip to the internal flipflops
      WRITE_DELAY			=> (period - 2 ns)		-- CLK is 2 ns longer than access delay for conservative padding for Real SRAM hold times and propagation delays from the internal flipflops to the external SRAM chip
    )
    port map	(
      -- Test bench control signals
      mem_clr				=> tb_mem_clr,
      mem_init			=> tb_mem_init,
      mem_dump			=> tb_mem_dump,
      verbose				=> tb_verbose,
      init_filename	=> tb_init_filename,
      dump_filename	=> tb_dump_filename,
      start_address	=> tb_start_address,
      last_address	=> tb_last_address,
                               
      -- Memory interface signals
      r_enable	=> RE,
      w_enable	=> WE,
      addr			=> ADDR,
      data			=> S_DATA
    );
                
  CLKGEN: process
  begin
    CLK <= '1';
    wait for period/2;
    CLK <= '0';
    wait for period/2;
  end process CLKGEN;
  
  CLKDIV: process
  begin
    DATA_OE <= '1';
    wait for period;
    DATA_OE <= '0';
    wait for 7 * period;
  end process CLKDIV;
  
  READFILE: process
    variable inline: line;
    variable str: string(1 to 8);
  begin
    EOF <= '0';
    EOL <= '0';
    
    wait until RST_N = '0';
    wait until RST_N = '1';
    
    wait for 200 ns;
    
    while not endfile(infile) loop
      -- read digital data from input file 
      readline(infile, inline);
      read(inline, str);
      file_line <= to_std_logic_vector(str);
      
      EOL <= '1';
      FULL_PACKET <= SYNC & PID & file_line & CRC;
      wait for period * fullBitLen * fullPacketLen;
      EOL <= '0';
    end loop;

    report "Reached end of "& filename;
    EOF <= '1';

    wait;
  end process READFILE;
  
  RESET: process
  begin
    RST_N <= '1';
    wait for period;
    RST_N <= '0';
    wait for period;
    RST_N <= '1';
    wait for 140 us;
  end process RESET;
  
  LOG_CTL: process
  begin
    LOG <= '0';
    wait for period;
    LOG <= '1';
    wait for 140 us;
    LOG <= '0';
    wait for 5.5 us;
  end process LOG_CTL;
  
  NRZI_OUT: process(DATA_OE)
  begin
    if(DATA_OE='1') then
      bit_count <= bit_count + 1;
    
      if(bit_count = fullPacketLen) then
        bit_count <= 0;
      
        D_PLUS_DEVICE <= '0';
        D_MINUS_DEVICE <= '0';
      elsif(bit_count < fullPacketLen-1) then
        D_PLUS_DEVICE <= FULL_PACKET(bit_count);
        D_MINUS_DEVICE <= not FULL_PACKET(bit_count);
      end if;
    end if;
  end process NRZI_OUT;
end TEST;
